<template>
  <el-row>
    <el-col :span="3">
      <VueDraggable
        v-model="componeList"
        :group="{ name: 'components', pull: 'clone', put: false }"
        item-key="id"
        :clone="handleClone"
        @end="onComponentsEnd"
        class="componentWarp"
      >
        <template v-for="item in componeList" :key="item.id">
          <!-- <el-menu-item :index="item.id + ''">
              <component :is="item.elementLabel"></component>
              <span>{{ item.elementName }}</span>
            </el-menu-item> -->
          <div class="componentList">
            <i>{{ item.elementIcon }}</i>
            <span>{{ item.elementName }}</span>
          </div>
        </template>
      </VueDraggable>
    </el-col>
    <el-col :span="21" class="grid-container">
      <!-- 网格背景 -->
      <div class="grid-bg" ref="gridBg" @click="cancelSelection">
        <!-- 拖拽区域 -->
        <vue-draggable
          group="components"
          v-model="cloneConmponents"
          class="drag-container"
          @end="onCanvasEnd"
          ghost-class="no-ghost"
        >
          <template v-for="item in cloneConmponents" :key="item.id">
            <component
              :is="item.elementLabel"
              :style="getItemStyle(item)"
              v-bind="item.props"
              :ref="item.ref"
              :class="{ selected: selectedId === item.id }"
              @click.stop="selectComponent(item.id)"
              >{{ item.elementName }}</component
            >
          </template>
        </vue-draggable>
      </div>
    </el-col>
  </el-row>
  <!--属性面板-->
  <el-drawer :model-value="drawerFlag" title="属性面板" @close="cancelSelection">
    <template v-if="attrList">
      <div v-for="item in attrList" :key="item.id">
        {{ item.attrName }}
        <select @change="changeAttrSelect(item.attrCode, $event)">
          <option
            v-for="attr in item.attrValueList"
            :key="attr.id"
            :value="attr.attrInfoValue"
            :selected="attr.defaultFlag === 'Y'"
          >
            {{ attr.attrInfoName }}
          </option>
        </select>
      </div>
    </template>
  </el-drawer>
</template>

<script setup lang="ts">
import type { ElementCloneInfo, ElementInfoVO } from '@/views/generator/model/GeneratorModel'
import { ElMessage } from 'element-plus'
import { computed, onMounted, reactive, ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
/**
 * 网格配置
 */
const gridConfig = reactive({
  size: 20, // 网格大小(px)
  color: '#e0e0e0', // 网格线颜色
  snap: true, // 是否启用吸附
  snapThreshold: 5, // 吸附阈值(px)
})

/**
 * 组件库原始组件
 */
const componeList = reactive<Array<ElementInfoVO>>([
  {
    id: 1,
    elementName: '按钮',
    elementIcon: '',
    elementLabel: 'el-button',
    elementDesc: '',
    nestingFlag: 'N',
    parentId: 0,
    children: null,
    elementAttrInfoList: [
      {
        id: 4,
        elementId: 1,
        attrCode: 'type',
        attrName: '按钮类型',
        attrValueList: [
          {
            id: 4,
            attrInfoId: 4,
            attrInfoValue: 'primary',
            attrInfoName: 'primary',
            defaultFlag: 'Y',
          },
          {
            id: 3,
            attrInfoId: 4,
            attrInfoValue: 'default',
            attrInfoName: '默认',
            defaultFlag: 'N',
          },
        ],
      },
    ],
  },
  {
    id: 2,
    elementName: '单选框',
    elementIcon: '',
    elementLabel: 'el-radio-group',
    elementDesc: '',
    elementTerminal: null,
    nestingFlag: 'Y',
    parentId: 0,
    children: [
      {
        id: 3,
        elementName: '单选框值',
        elementIcon: '',
        elementLabel: 'el-radio',
        elementDesc: '',
        nestingFlag: 'N',
        parentId: 2,
        children: null,
        elementAttrInfoList: [],
      },
    ],
    elementAttrInfoList: [
      {
        id: 3,
        elementId: 2,
        attrCode: 'disabled',
        attrName: '是否禁用',
        attrValueList: [
          { id: 2, attrInfoId: 3, attrInfoValue: 'false', attrInfoName: '否', defaultFlag: 'Y' },
          { id: 1, attrInfoId: 3, attrInfoValue: 'true', attrInfoName: '是', defaultFlag: 'N' },
        ],
      },
      { id: 2, elementId: 2, attrCode: 'value', attrName: '单选框的值', attrValueList: [] },
      { id: 1, elementId: 2, attrCode: 'v-model', attrName: '选中项绑定值', attrValueList: [] },
    ],
  },
]) // 保持原有组件数据不变

/**
 * 拖入后克隆的组件
 */
const cloneConmponents = ref<Array<ElementCloneInfo>>([])
/**
 * 画布ref
 */
const gridBg = ref<HTMLElement | null>(null)
/**
 * 保存已有元素的位置
 */
const gridIndex = new Map<string, ElementCloneInfo>()

/**
 * 组件样式计算
 * @param item
 */
const getItemStyle = (item: any) => {
  return {
    position: 'absolute',
    left: `${item.x * gridConfig.size}px`,
    top: `${item.y * gridConfig.size}px`,
  }
}
/**
 * 处理克隆对象时响应式对象属性仍与原对象属性绑定
 * @param source
 * @param hash
 */
function deepClone<T>(source: T, hash = new WeakMap()): T {
  if (source === null || typeof source !== 'object') {
    return source
  }

  // 处理循环引用
  if (hash.has(source)) return hash.get(source)

  // 处理特殊对象类型
  const clone = Array.isArray(source) ? [] : Object.create(Object.getPrototypeOf(source))
  hash.set(source, clone)

  // 递归克隆属性
  Object.keys(source).forEach((key) => {
    clone[key] = deepClone(source[key], hash)
  })

  return clone as T
}
/**
 * 克隆处理器
 * @param original 组件
 */
const handleClone = (original: ElementInfoVO) => {
  const props = analyzeAttr(original)
  const id = Date.now()
  const cloned: ElementCloneInfo = {
    ...reactive(deepClone(original)),
    id: id,
    props,
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    sizeCalculated: null,
  }
  // 创建尺寸计算完成的Promise
  const sizeCalculated = new Promise<void>((resolve) => {
    // 创建ref回调
    cloned.ref = (el) => {
      if (el) {
        const element = el.$el || el
        cloned.width = Math.ceil(element.clientWidth / gridConfig.size)
        cloned.height = Math.ceil(element.clientHeight / gridConfig.size)
        resolve() // 尺寸计算完成后resolve
      }
    }
  })
  cloned.sizeCalculated = sizeCalculated
  return cloned
}

/**
 * 画布拖拽结束处理
 * @param evt 鼠标事件
 */
const onCanvasEnd = (evt: any) => {
  // 更新组件位置（使用Vue3的响应式更新）
  const index = cloneConmponents.value.findIndex((c) => c.id === evt.data.id)
  if (index !== -1) {
    //计算x.y坐标
    calculateCoordinates(cloneConmponents.value[index], evt)
  }
}
/**
 * 组件库拖拽进入画布
 * @param evt
 */
const onComponentsEnd = async (evt: any) => {
  // 获取组件位置
  const index = cloneConmponents.value.findIndex((c) => c.id === evt.clonedData.id)
  if (index !== -1) {
    // 等待尺寸计算完成
    await cloneConmponents.value[index].sizeCalculated
    //计算x.y坐标
    const successFlag = calculateCoordinates(cloneConmponents.value[index], evt)
    if (!successFlag) {
      cloneConmponents.value = cloneConmponents.value.filter((item) => item.id != evt.clonedData.id)
    }
  }
}

/**
 * 计算坐标
 */
const calculateCoordinates = (component: ElementCloneInfo, evt: any) => {
  if (gridConfig.snap && gridBg.value) {
    const containerRect = gridBg.value.getBoundingClientRect()
    // 使用拖拽事件的最新坐标
    const relativeX = evt.originalEvent.clientX - containerRect.left - gridConfig.size
    const relativeY = evt.originalEvent.clientY - containerRect.top - gridConfig.size

    // 网格对齐计算
    const snapX = Math.round(relativeX / gridConfig.size) * gridConfig.size
    const snapY = Math.round(relativeY / gridConfig.size) * gridConfig.size
    //获取组件位置
    const dragX = snapX / gridConfig.size
    const dragY = snapY / gridConfig.size
    if (dragX < 0 || dragY < 0) {
      ElMessage.error('元素位置错误，请重新拖动')
      return false
    }
    // 检查新位置是否可用
    if (!isPositionAvailable(component, dragX, dragY)) {
      ElMessage.error('该位置已被其他组件占用')
      return false
    }

    // 清除旧位置标记
    clearOldPosition(component)
    component.x = dragX
    component.y = dragY
    // 标记新位置
    markNewPosition(component)
    return true
  }
}
/**
 * 检查位置是否可用
 * @param component
 * @param x
 * @param y
 */
const isPositionAvailable = (component: ElementCloneInfo, x: number, y: number) => {
  for (let i = x; i < x + component.width; i++) {
    for (let j = y; j < y + component.height; j++) {
      const key = `${i},${j}`
      if (gridIndex.has(key) && gridIndex.get(key)?.id !== component.id) {
        return false
      }
    }
  }
  return true
}

/**
 * 清除旧位置标记
 * @param component
 */
const clearOldPosition = (component: ElementCloneInfo) => {
  for (let i = component.x; i < component.x + component.width; i++) {
    for (let j = component.y; j < component.y + component.height; j++) {
      const key = `${i},${j}`
      if (gridIndex.get(key)?.id === component.id) {
        gridIndex.delete(key)
      }
    }
  }
}

/**
 * 标记新位置
 * @param component
 */
const markNewPosition = (component: ElementCloneInfo) => {
  for (let i = component.x; i < component.x + component.width; i++) {
    for (let j = component.y; j < component.y + component.height; j++) {
      const key = `${i},${j}`
      gridIndex.set(key, component)
    }
  }
}
/**
 * 解析属性
 */
const analyzeAttr = (component: ElementInfoVO) => {
  const props = {}
  const attrList = component.elementAttrInfoList
  if (attrList) {
    attrList.forEach((item) => {
      const valueList = item.attrValueList
      const attrValue = valueList?.find((item) => item.defaultFlag === 'Y')
      props[item.attrCode] = attrValue?.attrInfoValue
    })
  }
  if (component.children) {
    component.children.forEach((item) => {
      analyzeAttr(item)
    })
  }
  return props
}
/**
 * 选中组件标记
 */
const selectedId = ref<number | null>(null)
/**
 * 选中组件
 * @param id 选中组件id
 */
const selectComponent = (id: number) => {
  selectedId.value = id
}

/**
 * 取消选中
 */
const cancelSelection = () => {
  selectedId.value = null
}
/**
 * 计算属性判断是否打开抽屉
 */
const drawerFlag = computed(() => {
  return selectedId.value != null
})
/**
 * 实时获取组件属性
 */
const attrList = computed(() => {
  if (!selectedId.value) {
    return null
  }
  const compone: ElementCloneInfo = cloneConmponents.value.filter(
    (item) => item.id === selectedId.value
  )[0]
  return compone.elementAttrInfoList
})
/**
 * 修改属性
 */
const changeAttrSelect = (attrCode: string, attrValue: any) => {
  const compone: ElementCloneInfo = cloneConmponents.value.filter(
    (item) => item.id === selectedId.value
  )[0]
  compone.props[attrCode] = attrValue.target.value
  const attrCodeInfo = compone.elementAttrInfoList.filter((item) => item.attrCode == attrCode)
  attrCodeInfo[0].attrValueList.forEach((item) => {
    if (item.attrInfoValue === attrValue.target.value) {
      item.defaultFlag = 'Y'
    } else {
      item.defaultFlag = 'N'
    }
  })
}
onMounted(() => {
  // 初始化网格背景
  if (gridBg.value) {
    gridBg.value.style.backgroundSize = `${gridConfig.size}px ${gridConfig.size}px`
    gridBg.value.style.backgroundImage = `
      linear-gradient(${gridConfig.color} 1px, transparent 1px),
      linear-gradient(90deg, ${gridConfig.color} 1px, transparent 1px)`
  }
})
</script>

<style scoped>
.el-menu {
  height: 100vh;
}

.grid-container {
  position: relative;
  height: 100vh;
}

.grid-bg {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: auto;
}
.drag-container {
  position: relative;
  width: 100%;
  height: 100%;
}

.draggable-item {
  position: absolute;
  background: rgba(64, 158, 255, 0.1);
  border: 1px dashed #409eff;
  transition: transform 0.1s ease;
  z-index: 1;
}

.draggable-item:hover {
  border: 1px solid #409eff;
  box-shadow: 0 0 8px rgba(64, 158, 255, 0.3);
}
/**禁用element-plus 第二个button按钮默认添加外边距 */
.drag-container :deep(.el-button + .el-button) {
  margin-left: 0;
}
/**禁用幽灵组件样式 */
.no-ghost {
  display: none !important;
}
/* 选中组件样式 */
.selected {
  border: 2px solid #409eff !important;
  box-shadow: 0 0 8px rgba(64, 158, 255, 0.3);
  z-index: 10;
}

/* 选中标识角标 */
.selected::after {
  content: '✓';
  position: absolute;
  top: -8px;
  right: -8px;
  width: 16px;
  height: 16px;
  background: #409eff;
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
}
/**组件库 */
.componentWarp {
  display: flex;
  flex-wrap: wrap;
  gap: 10px; /* 组件间距 */
  padding-top: 10px;
  height: 100vh;
  justify-content: space-evenly;
  overflow-y: auto;
}
/**组件样式 */
.componentList {
  flex: 0 0 calc(50% - 10px); /* 每行两个组件，减去间距 */
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding: 10px;
  border-radius: 4px;
  transition: all 0.3s ease;
  cursor: pointer;
  text-align: center;
  /* min-height: 80px; */
  height: 30px;
  box-sizing: border-box;
  background-color: #f5f7fa; /* 更柔和的背景色 */
  border: 1px solid #ebeef5;
  color: #606266; /* 文字颜色 */
  font-size: 14px;
  line-height: 40px;
}
/* 图标样式 */
.componentList i {
  font-size: 24px;
  margin-bottom: 8px;
  color: #409eff; /* 主色调 */
}

/* 悬停效果 */
.componentList:hover {
  background-color: #ecf5ff;
  border-color: #c6e2ff;
  transform: translateY(-2px);
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

/* 点击效果 */
.componentList:active {
  transform: translateY(0);
}
</style>
